package resml

import (
	"io"
	"net/url"
)

// 使用 Storage 作为本地缓存的资源
type CachedResource struct {
	remote  Resource // 远端/服务器端的资源
	path    string   // 本地存储的资源的路径
	storage Storage  // 本地存储的资源

	progress Progress // 获取资源的读取进度

	remoteWritable bool // 远端/服务器端的资源是否或写
}

// 创建一个 CachedResource。Storage 仅用于缓存，而不用于定义资源。
func NewCachedResource(storage Storage, remote Resource, remoteWritable bool) *CachedResource {
	uri, _ := url.Parse(remote.Uri())
	return &CachedResource{storage: storage, path: uri.Path, remote: remote, remoteWritable: remoteWritable}
}

// 获取资源的 URI
func (r *CachedResource) Uri() string {
	return r.remote.Uri()
}

// 获取资源的元数据
func (r *CachedResource) Meta() (meta Meta, err error) {
	meta, err = r.remote.Meta() // 优先使用远程资源的元数据
	if err == nil || r.storage == nil {
		return
	}
	if meta, es := r.storage.Meta(r.path); es != nil {
		return nil, NewMultiError(err, es)
	} else {
		return meta, nil
	}
}

// 获取资源
func (r *CachedResource) Get() (reader io.ReadCloser, meta Meta, err error) {
	if r.storage == nil {
		return r.remote.Get()
	}
	r.progress = NewEmptyProgress()
	meta, err = r.remote.Meta()
	if err != nil {
		return nil, nil, err
	}
	reader, lmeta, err := r.storage.Get(r.path)
	if err != nil || lmeta == nil || !MetaEtagSizeEquals(lmeta, meta) {
		if reader != nil {
			reader.Close() // 关闭本地 reader
		}
		// 远程资源与本地资源不一致（未下载/有更新），需要重新下载并缓存起来
		var rr io.ReadCloser
		rr, meta, err = r.remote.Get()
		if err != nil {
			return
		}
		defer rr.Close()
		pr := NewProgressReader(rr, meta.ContentLength())
		r.progress = pr // 设置读取进度
		if err = r.storage.Save(r.path, meta, pr); err != nil {
			return r.remote.Get() // 如果缓存失败，则重新下载并返回
		}
		return r.storage.Get(r.path) // 下载成功，返回本地缓存
	}
	return // 远程资源与本地资源一致，则返回
}

// 创建/更新资源。
func (r *CachedResource) Put(meta Meta, reader io.Reader) error {
	if r.storage != nil {
		r.storage.Remove(r.path) // 删除本地缓存
	}
	if !r.remoteWritable {
		return ErrRemoteReadonly
	}
	return r.remote.Put(meta, reader)
}

// 删除资源
func (r *CachedResource) Remove() error {
	if r.storage == nil && r.remoteWritable {
		return r.remote.Remove()
	}
	var es, er error
	es = r.storage.Remove(r.path)
	if r.remoteWritable {
		er = r.remote.Remove()
	}
	return SelectError(es, er)
}

// 实现 Progress 接口 ------------------------>

// 读取/下载资源的进度，如果没有，则返回 nil。
// 在调用 Get() 之后，如果本地没有缓存，在开始下载时，会返回进度。
func (r *CachedResource) GetProgress() Progress {
	return r.progress
}

// 获取已读取的字节数
func (r *CachedResource) Current() int64 {
	if r.progress == nil {
		return 0
	}
	return r.progress.Current()
}

// 总字节数
func (r *CachedResource) Total() int64 {
	if r.progress == nil {
		return 0
	}
	return r.progress.Total()
}

// 获取进度百分比
func (r *CachedResource) Percent() float64 {
	if r.progress == nil {
		return 0
	}
	return r.progress.Percent()
}

// <------------------------ 实现 Progress 接口
